package com.iakuil.app.common.restful.doc;

import com.fasterxml.jackson.databind.type.SimpleType;
import com.iakuil.app.common.core.DictEnum;
import com.iakuil.app.common.core.util.ObjectUtils;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.util.PrimitiveType;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import org.springdoc.core.customizers.PropertyCustomizer;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 字典枚举属性自定义配置
 *
 * @author Kai
 */
@Component
public class DictEnumPropertyCustomizer extends AbstractDictEnumCustomizer implements PropertyCustomizer {
    @Override
    public Schema<?> customize(Schema property, AnnotatedType type) {
        if (type.getType() instanceof SimpleType fieldType) {
            Class<?> fieldClazz = fieldType.getRawClass();

            if (DictEnum.class.isAssignableFrom(fieldClazz)) {
                if (fieldClazz.getGenericInterfaces()[0] instanceof ParameterizedType parameterizedType) {

                    // 通过父接口获取泛型中枚举值的class类型
                    Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
                    Schema<Object> schema = getSchemaByType(actualTypeArgument, property);

                    // 重新设置字段的注释和默认值
                    schema.setEnum(super.getValues(fieldClazz));

                    // 扩展描述文本
                    String originalDesc = property.getDescription();
                    String appendedDesc = this.getDescription(fieldClazz);
                    if (ObjectUtils.isEmpty(originalDesc)) {
                        schema.setDescription(appendedDesc);
                    } else {
                        schema.setDescription(originalDesc + " (" + appendedDesc + ")");
                    }
                    return schema;
                }
            }
        }
        return property;
    }

    /**
     * 根据枚举值的类型获取对应的 {@link Schema} 类
     * 这么做是因为当SpringDoc获取不到属性的具体类型时会自动生成一个string类型的 {@link Schema} ，
     * 所以需要根据枚举值的类型获取不同的实例，例如 {@link io.swagger.v3.oas.models.media.IntegerSchema}、
     * {@link io.swagger.v3.oas.models.media.StringSchema}
     *
     * @param type         枚举值的类型
     * @param sourceSchema 从属性中加载的 {@link Schema} 类
     * @return 获取枚举值类型对应的 {@link Schema} 类
     */
    @SuppressWarnings({"unchecked"})
    public Schema<Object> getSchemaByType(Type type, Schema<?> sourceSchema) {
        Schema<Object> schema;
        PrimitiveType item = PrimitiveType.fromType(type);

        if (item == null) {
            schema = new ObjectSchema();
        } else {
            schema = item.createProperty();
        }

        // 获取schema的type和format
        String schemaType = schema.getType();
        String format = schema.getFormat();
        // 复制原schema的其它属性
        BeanUtils.copyProperties(sourceSchema, schema);

        // 使用根据枚举值类型获取到的schema
        return schema.type(schemaType).format(format);
    }
}
